package com.fanml.xyz;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import javax.sql.DataSource;

/**
 * @ClassName: HandleDbUtil
 * @Description: TODO 批量迁移数据的工具类
 * @author: ChuanHui
 * @date: 2020年9月17日 下午2:01:45
 */
public class HandleDbUtil {

    private static int threadCount_Max = 32; //默认最大线程数

    public static void main(String[] args) {

    }

    /**
     * @param sql
     * @param tableName
     * @return HandleDbSql
     * @Title: getHandleDbSql
     * @Description: 一些sql语句的基础封装
     */
    public static HandleDbSql getHandleDbSql(String sql, String tableName) {
        return new HandleDbSql(sql, tableName);
    }

    /**
     * @param handleDbSql
     * @param dataMap
     * @param prepareStatement
     * @throws SQLException
     * @Title: addBatchPrepareStatement
     * @Description: TODO
     * @return: void
     */
    public static void addBatchPrepareStatement(HandleDbSql handleDbSql, Map<String, Object> dataMap,
                                                final PreparedStatement prepareStatement) throws SQLException {
        List<String> listColumnName = handleDbSql.getListColumnName();
        for (int i = 0; i < handleDbSql.getColumnCount(); i++) {
            String columnName = listColumnName.get(i);
            prepareStatement.setObject(i + 1, dataMap.get(columnName));
        }
        prepareStatement.addBatch();
    }

    /**
     * @param sourceConnection
     * @param sql
     * @return List<Map < String, Object>>
     * @throws SQLException
     * @Description: 单线程获取目标数据
     */
    public static List<Map<String, Object>> startSingleServer(Connection sourceConnection, String sql)
            throws SQLException {
        List<Map<String, Object>> list = HandleThreadRunable.executeQuery(sourceConnection, sql);
        if (sourceConnection != null) {
            sourceConnection.close();
        }
        return list;
    }

    /**
     * @param dataCount        数据总数
     * @param pageSize         页大小
     * @param threadCount      线程数
     * @param sql              抽取的语句 eg. SELECT NAME AS XM,XX AS A FROM DUAL
     *                         字段必须满足别名形式
     * @param sourceDataSource 源数据
     * @param targetDataSource 目标数据
     * @param className        classspath业务所在类
     * @param args             classspath业务所在类 构造方法所需参数
     * @throws SQLException
     * @Title: startThreadServer
     * @Description: TODO 批量获取数据
     * @return: void
     */
    public static void startThreadServer(int dataCount, int pageSize, int threadCount, String sql,
                                         DataSource sourceDataSource, DataSource targetDataSource, String className, Map<String, Object> args)
            throws SQLException {
        threadCount = (threadCount > threadCount_Max ? 8 : threadCount);
        int total = dataCount % pageSize == 0 ? dataCount / pageSize : dataCount / pageSize + 1;
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        Connection sourceConnection = null;
        try {
            if (sourceDataSource == null)
                return;
            sourceConnection = sourceDataSource.getConnection(); // 源数据源 只用做查询
        } catch (SQLException e) {
            throw new SQLException("sourceDataSource is error." + e.getMessage());
        }
        for (int i = 1; i <= total; i++) {
            int start = (i - 1) * pageSize;
            int end = i * pageSize;
            if (i == total) {
                end = dataCount;
            }
            String autoSqlPage = HandleDbSql.autoPageSql(sql, start, end);// 自动分页sql
            Connection targetConnection = null;
            try {
                if (targetDataSource != null)
                    targetConnection = targetDataSource.getConnection();

                HandleDataMart handleDataMart = null;
                Class<? extends HandleDataMart> subclass = Class.forName(className).asSubclass(HandleDataMart.class);
                Constructor<? extends HandleDataMart> constructor = subclass.getConstructor(Map.class);
                handleDataMart = constructor.newInstance(args);
                Field field = subclass.getSuperclass().getDeclaredField("targetConnection");
                field.setAccessible(true);
                field.set(handleDataMart, targetConnection);// 目标数据源
                HandleThreadRunable<HandleDataMart> threadRunables = new HandleThreadRunable<>(autoSqlPage,
                        sourceConnection, countDownLatch);// 根据旧库sql语句和连接池获取数据
                threadRunables.setObject(handleDataMart);
                HandleThreadPoolUtil.getInstance().submit(threadRunables);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (i % threadCount == 0) {
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                countDownLatch = new CountDownLatch(threadCount);
            } else if (i == total) {
                try {
                    int k = total % threadCount;
                    int y = threadCount - k;
                    for (int x = 0; x < y; x++) {
                        countDownLatch.countDown();
                    }
                    countDownLatch.await();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }
        try {

        } finally {
            if (sourceConnection != null) {
                try {
                    sourceConnection.close();
                } catch (SQLException e) {
                }
            }
            HandleThreadPoolUtil.getInstance().shutdown();
        }
    }

}
